﻿using DevExpress.Data;
using DevExpress.Data.Filtering;
using DevExpress.Data.Linq;
using DevExpress.Data.Linq.Helpers;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Utils;
using DevExpress.Spreadsheet;
using System.Data;
using System.Linq.Dynamic.Core;
using System.Reflection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Services;
using YAPACS.BusinessObjects.Exams;
using YAPACS.BusinessObjects.Managements.Exams;
using YAPACS.Organizations;
using YAPACS.RisServer.Abstractions;
using YAPACS.RisServer.Services.Dtos;
using YAPACS.Workflows;

namespace YAPACS.RisServer.Services;

public class ExportService(IRepository<ExamFilter, Guid> filterRepository, IRepository<ApplicationUser, Guid> userRepository,
    ICurrentUserOrganization currentUserOrganization, IRepository<Exam, Guid> examRepository, IServiceProvider serviceProvider,
    IRepository<ExamFlag, Guid> examFlagRepository)
    : DomainService, IExportService, ITransientDependency
{
    private readonly IRepository<ExamFilter, Guid> _filterRepository = filterRepository;
    private readonly IRepository<ApplicationUser, Guid> _userRepository = userRepository;
    private readonly ICurrentUserOrganization _currentUserOrganization = currentUserOrganization;
    private readonly IRepository<Exam, Guid> _examRepository = examRepository;
    private readonly IServiceProvider _serviceProvider = serviceProvider;
    private readonly IRepository<ExamFlag, Guid> _examFlagRepository = examFlagRepository;

    private static readonly Dictionary<Type, Dictionary<object, string>> _enumDescriptionCache = [];

    public async Task<MemoryStream> ExportListToStreamAsync(DataGridRequest request, CriteriaOperator criteriaOperator,
        IWorkflow expressionWorkflow, IWorkflow getHeadersWorkflow, IWorkflow getDefaultFilterWorkflow = null,
        ExamFilterType examFilterType = ExamFilterType.Appointment)
    {
        //获取查询表达式
        ExamFilter examFilter = null; ExamFlag examFlag = null;
        if (request.UserData != null && request.UserData.Count > 0)
        {
            const string key = "Output";
            var input = new WorkflowInput { { "Input", request.UserData } };
            var workflowOutput = await expressionWorkflow.InvokeAsync(input);
            if (!workflowOutput.Success)
            {
                throw new Exception(workflowOutput.Error);
            }
            if (input.ContainsKey(key) && input.GetValueOrDefault(key) is CriteriaOperator criteria)
            {
                criteriaOperator = CriteriaOperator.And(criteriaOperator, criteria);
            }
        }
        else
        {
            if (!request.FilterId.Equals(default))
            {
                examFilter = await _filterRepository.FindAsync(request.FilterId);
                if (examFilter == null)
                {
                    //查询收藏
                    examFlag = await _examFlagRepository.GetAsync(request.FilterId);
                }
            }
            else
            {
                var user = await _userRepository.GetAsync(_currentUserOrganization.UserId);
                examFilter = user.GetDefaultFilter(_currentUserOrganization.UserGroupId.Value, examFilterType);
            }
            criteriaOperator = CriteriaOperator.And(criteriaOperator, await GetFilter(getDefaultFilterWorkflow, examFilter));
            if (examFlag != null)
            {
                criteriaOperator = CriteriaOperator.And(criteriaOperator, CriteriaOperator.FromLambda<Exam>(e => e.ExamFlags.Any(f => f == examFlag)));
            }
        }
        //获取表头
        var columnsOutput = await getHeadersWorkflow.InvokeAsync(new WorkflowInput { { "Filter", examFilter } });
        if (!columnsOutput.Success)
        {
            throw new Exception(columnsOutput.Error);
        }
        string columnsString = columnsOutput.Data as string;

        var columns = columnsString.Deserialize<Column[]>();

        //根据获取到的表头查询列表
        var selectStr = columns.GetExportColumnString();
        //获取排序
        if (request.Sort != null && request.Sort.Count > 0)
        {
            foreach (var sort in request.Sort)
            {
                var column = columns.Where(e => e.DataField == sort.Selector).FirstOrDefault();
                if (column != null)
                {
                    if (!string.IsNullOrWhiteSpace(column.Order))
                    {
                        sort.Selector = column.Order;
                    }
                    else if (!string.IsNullOrWhiteSpace(column.Property))
                    {
                        sort.Selector = column.Property;
                    }
                }
            }
        }
        else
        {
            request.Sort = [new() { Selector = "Oid", Desc = true }];
        }
        var orders = request.Sort.Select(e => new ServerModeOrderDescriptor(new OperandProperty(e.Selector), e.Desc));
        var query = (await _examRepository.GetQueryableAsync()).AppendWhere(new CriteriaToExpressionConverter(), criteriaOperator)
            .MakeOrderBy(new CriteriaToExpressionConverter(), orders);
        //获取总行数
        var count = CriteriaToQueryableExtender.Count(query);
        //分页查询
        //写入excel
        //var skipQuery = CriteriaToQueryableExtender.Skip(query, request.Skip);
        //var list = CriteriaToQueryableExtender.Take(skipQuery, request.Take).Select(selectStr).ToDynamicList();
        var list = query.Select(selectStr).ToDynamicList();
        var dataSource = ToDataTable(list);
        return CreateWorkbook(dataSource);
    }

    public async Task<string> ExportListToFileAsync(DataGridRequest request, CriteriaOperator criteriaOperator,
        IWorkflow expressionWorkflow, IWorkflow getHeadersWorkflow, IWorkflow getDefaultFilterWorkflow = null,
        ExamFilterType examFilterType = ExamFilterType.Appointment)
    {
        using var stream = await ExportListToStreamAsync(request, criteriaOperator, expressionWorkflow, getHeadersWorkflow, getDefaultFilterWorkflow, examFilterType);
        var tempFile = Path.GetTempFileName();
        using var fileStream = new FileStream(tempFile, FileMode.Create, FileAccess.Write);
        stream.Seek(0, SeekOrigin.Begin);
        await stream.CopyToAsync(fileStream);
        return tempFile;
    }

    private async Task<CriteriaOperator> GetFilter(IWorkflow getDefaultFilterWorkflow, ExamFilter examFilter)
    {
        CriteriaOperator criteria = null;

        if (getDefaultFilterWorkflow != null)
        {
            //调用工作流
            const string key = "Output";
            var input = new WorkflowInput();
            var workflowOutput = await getDefaultFilterWorkflow.InvokeAsync(input);
            if (workflowOutput.Success && input.ContainsKey(key) && input.GetValueOrDefault(key) is CriteriaOperator criteria2)
            {
                if (criteria2 is not null)
                {
                    criteria = criteria2;
                }
            }
        }
        if (criteria is null && examFilter != null)
        {
            criteria = CriteriaOperator.Parse(examFilter.Filter);
        }
        if (criteria is not null)
        {
            criteria = CriteriaOperatorPatcherBase.PatchCriteria(_serviceProvider, criteria);
        }

        return criteria;
    }

    private static Type GetRealType(Type type, out bool nullable)
    {
        nullable = false;
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            type = type.GenericTypeArguments[0];
            nullable = true;
        }
        return type;
    }

    private static IEnumerable<string> GetEnumValueTexts(Type enumType)
    {
        if (enumType.IsEnum)
        {
            var enumDescriptor = new EnumDescriptor(EnumDescriptor.GetEnumType(enumType));
            foreach (var value in enumDescriptor.Values)
            {
                yield return enumDescriptor.GetCaption(value);
            }
        }
    }

    private static DataTable ToDataTable(List<dynamic> items)
    {
        var dataTable = new DataTable();

        if (items == null || items.Count == 0)
        {
            return dataTable;
        }

        // 使用第一个 dynamic 对象获取列名
        var firstItem = items[0];
        var properties = (firstItem.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) as PropertyInfo[])
            .Where(p => p.Name != "Item")
            .ToList();

        foreach (var prop in properties)
        {
            dataTable.Columns.Add(prop.Name);

            var type = GetRealType(prop.PropertyType, out bool nullable);
            if (type.IsEnum && !_enumDescriptionCache.ContainsKey(type))
            {
                var enumDescriptor = new EnumDescriptor(type);
                var enumValues = enumDescriptor.Values;
                var enumDic = new Dictionary<object, string>();
                var enumValueTexts = GetEnumValueTexts(type).ToList();
                for (var i = 0; i < enumValues.Length; i++)
                {
                    if (enumValueTexts.Count > i)
                    {
                        enumDic[enumValues.GetValue(i)] = enumValueTexts[i];
                    }
                }
                _enumDescriptionCache[type] = enumDic;
            }
        }

        // 添加数据行
        foreach (var item in items)
        {
            var row = dataTable.NewRow();

            foreach (var prop in properties)
            {
                var value = prop.GetValue(item, null);

                if (GetRealType(prop.PropertyType, out bool nullable) == typeof(bool))
                {
                    row[prop.Name] = value == null ? "" : value ? "是" : "否";
                }
                else if (GetRealType(prop.PropertyType, out nullable) == typeof(DateTime))
                {
                    row[prop.Name] = $"{value:yyyy-MM-dd HH:mm:ss}";
                }
                else if (GetRealType(prop.PropertyType, out nullable).IsEnum)
                {
                    if (value != null)
                    {
                        var enumDic = _enumDescriptionCache[GetRealType(prop.PropertyType, out nullable)];
                        row[prop.Name] = enumDic[value];
                    }
                }
                else
                {
                    row[prop.Name] = prop.GetValue(item, null);
                }
            }

            dataTable.Rows.Add(row);
        }

        return dataTable;
    }

    private static MemoryStream CreateWorkbook(DataTable dataTable)
    {
        using var workbook = new Workbook();
        var worksheet = workbook.Worksheets.First();
        CreateHeaderRow(worksheet, dataTable.Columns);
        CreateDataRows(worksheet, dataTable);
        var stream = new MemoryStream();
        workbook.SaveDocument(stream, DocumentFormat.Xlsx);
        return stream;
    }

    private static void CreateHeaderRow(Worksheet worksheet, DataColumnCollection dataColumnCollection)
    {
        var sheetColumnIndex = 0;
        var startRow = 0;
        foreach (DataColumn item in dataColumnCollection)
        {
            var sheetColumn = worksheet.Columns[sheetColumnIndex];
            var sheetCell = worksheet.Cells[startRow, sheetColumnIndex++];
            sheetCell.Value = item.ColumnName;
        }
    }

    private static void CreateDataRows(Worksheet worksheet, DataTable dataTable)
    {
        var startRow = 1;
        foreach (DataRow row in dataTable.Rows)
        {
            var sheetColumnIndex = 0;
            foreach (DataColumn column in dataTable.Columns)
            {
                var sheetCell = worksheet[startRow, sheetColumnIndex];
                var value = row[column];
                sheetCell.SetValue(value);
                sheetColumnIndex++;
            }
            startRow++;
        }
    }
}
